using System;
using System.Collections;
using Kino.Glitch;
using UnityEngine;
using UnityEngine.UI;

namespace Player
{
    [RequireComponent(typeof(PlayerMovement), typeof(PlayerDropBomb))]
    public class Player : MonoBehaviour
    {
        [Header("References")]
        [SerializeField] private Managers.StatisticManager _stats;
        [SerializeField] private HealthBar _healthBar;
        [SerializeField] private Image _diePanel;
        [SerializeField] private AudioSource _deathSound;
        [Space]
        [SerializeField] private PlayerMovement _movement;
        [SerializeField] private PlayerDropBomb _dropBomb;
        [SerializeField] private DigitalGlitch _glitch;
        [SerializeField] private Text _diePanelText;
        [Header("Other")]
        [SerializeField, Min(1)] private int _maxHealth;
        
        public bool IsDied { get; private set; }
        
        private int _health;
        private static readonly int _isFlying
            = Animator.StringToHash("isFlying");

        private void Start()
        {
            _health = _maxHealth;
            _healthBar.SetValue(_health, _maxHealth);
        }

        private void OnValidate()
        {
            _movement ??= GetComponent<PlayerMovement>();
            _dropBomb ??= GetComponent<PlayerDropBomb>();
            try
            {
                _diePanelText ??= _diePanel.GetComponentInChildren<Text>();
            }
            catch (NullReferenceException)
            {
                // pass
            }
            // ReSharper disable once PossibleNullReferenceException
            try
            {
                _glitch ??= Camera.main.GetComponent<DigitalGlitch>();
            }
            catch
            {
                // pass
            }
        }

        /// <summary>
        /// Наносит определенное количество урона.
        /// Если у игрока отрицательное количество здоровья, становится 0.
        /// Если у игрока 0 здоровья, игрок умирает по причине `dieReason`.
        /// </summary>
        /// <param name="damage">урон</param>
        /// <param name="dieReason">причина смерти</param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// поднимается, если урон отрицательный</exception>
        public void TakeDamage(int damage, string dieReason)
        {
            if (damage < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(damage));
            }
            _health -= damage;
            if (_health <= 0)
            {
                Kill(dieReason);
                return;
            }
            _healthBar.SetValue(_health, _maxHealth);
        }

        /// <summary>
        /// Убивает игрока по причине `dieReason` и повышает количество смертей
        /// </summary>
        /// <param name="dieReason">причина смерти</param>
        public void Kill(string dieReason)
        {
            if (IsDied) return;
            IsDied = true;
            _health = 0;
            _stats.IncreaseDeathCount();
            _healthBar.SetValue(0, _maxHealth);
            _stats.SaveStatistic();
            StartCoroutine(DieCoroutine(dieReason));
        }

        private IEnumerator DieCoroutine(string dieReason)
        {
            _movement.FlightSound.Stop();
            _movement.Anim.SetBool(_isFlying, false);
            _movement.enabled = false;
            _dropBomb.enabled = false;
            _deathSound.Play();

            while (_deathSound.isPlaying)
            {
                _glitch.intensity = Mathf.Lerp(
                    0f,
                    0.75f,
                    _deathSound.time / _deathSound.clip.length
                );
                yield return new WaitForSeconds(0.1f);
            }

            _glitch.intensity = 0f;

            _diePanelText.text = $"<i>Сдох</i>, так как <b>{dieReason}</b>";
            _diePanel.gameObject.SetActive(true);
            enabled = false;
        }
    }
}
